using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using My3DGame.Game_Base.Cameras;


namespace My3DGame
{
    /// <summary>
    /// This is a game component that implements IUpdateable.
    /// </summary>
    public class clsMenuModelManager
    {
        Random rand = new Random();
        private clsCameraBackground camera;
        private Game1 game1;

        //khai bao doi tuong clsSky
        clsSky clsSky;
        clsTerrain clsTerrain;
        // List of models
        List<clsBasicModel> models = new List<clsBasicModel>();

        // Spawn variables
        Vector3 maxSpawnLocation = new Vector3(100, 100, -3000);
        int nextSpawnTime = 0;
        int timeSinceLastSpawn = 0;
        float maxRollAngle = MathHelper.Pi / 40;

        // Enemy count
        int enemiesThisLevel = 0;

        // Misses variables
        int missedThisLevel = 0;

        // Current level
        int currentLevel = 0;

        // List of LevelInfo objects
        List<LevelInfo> levelInfoList = new List<LevelInfo>(  );

        // Shot stuff
        List<clsBasicModel> shots = new List<clsBasicModel>(  );
        float shotMinZ = -3000;

        //Explosion stuff
        List<ParticleExplosion> explosions = new List<ParticleExplosion>();
        ParticleExplosionSettings particleExplosionSettings = new ParticleExplosionSettings();
        ParticleSettings particleSettings = new ParticleSettings();
        Texture2D explosionTexture;
        Texture2D explosionColorsTexture;
        Effect explosionEffect;

        // Star sheet stuff
        ParticleStarSheet stars;
        Effect starEffect;
        Texture2D starTexture;


        // Powerup stuff
        public int consecutiveKills = 0;

        public int missesLeft
        {
            get
            {
                return
                    levelInfoList[currentLevel].missesAllowed
                    - missedThisLevel;
            }
        }

        public clsMenuModelManager(Game game, clsCameraBackground camera, Game1 game1)
        {
            // Initialize game levels
            levelInfoList.Add(new LevelInfo(0, 100, 20, 2, 6, 10));
            levelInfoList.Add(new LevelInfo(900, 2800, 22, 2, 6, 9));
            levelInfoList.Add(new LevelInfo(800, 2600, 24, 2, 6, 8));
            levelInfoList.Add(new LevelInfo(700, 2400, 26, 3, 7, 7));
            levelInfoList.Add(new LevelInfo(600, 2200, 28, 3, 7, 6));

            this.camera = camera;
            this.game1 = game1;
            LoadContent();
        }

        /// <summary>
        /// Allows the game component to perform any initialization it needs to before starting
        /// to run.  This is where it can query for any required services and load content.
        /// </summary>
        public void Initialize()
        {
            // Set initial spawn time
            SetNextSpawnTime();
        }

        protected void LoadContent()
        {
            // Load explosion textures and effect
            explosionTexture = game1.Content.Load<Texture2D>(@"Textures\Particle");
            explosionColorsTexture = game1.Content.Load<Texture2D>(@"Textures\ParticleColors");
            explosionEffect = game1.Content.Load<Effect>(@"effects\particle");

            // Set effect parameters that don't change per particle
            explosionEffect.CurrentTechnique = explosionEffect.Techniques["Technique1"];
            explosionEffect.Parameters["theTexture"].SetValue(explosionTexture);

            // Load star texture and effect
            starTexture = game1.Content.Load<Texture2D>(@"textures\Stars");
            starEffect = explosionEffect.Clone();
            starEffect.CurrentTechnique = starEffect.Techniques["Technique1"];
            starEffect.Parameters["theTexture"].SetValue(explosionTexture);

            // Initialize particle star sheet
            stars = new ParticleStarSheet(
                game1.GraphicsDevice,
                new Vector3(2000, 2000, -1900),
                1500, starTexture,
                particleSettings,
                starEffect);

            //load content for clsSky
            clsSky = game1.Content.Load<clsSky>("sky");
            //load content for clsTerrain
            clsTerrain = new clsTerrain(game1);
            clsTerrain.loadContent(game1.Content);
        }

        /// <summary>
        /// Allows the game component to update itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public void Update(GameTime gameTime)
        {
            // Check to see if it's time to spawn
            CheckToSpawnEnemy(gameTime);

            //if (camera.CameraDirection.X > -0.9f)
            //{
            //    camera.CameraDirection = Vector3.Transform(camera.CameraDirection, Matrix.CreateFromAxisAngle(
            //        Vector3.Cross(Vector3.Right, camera.CameraDirection), 0.005f));
            //}
            //else
            //{
            //    camera.CameraDirection = new Vector3(0.9f, camera.CameraDirection.Y, camera.CameraDirection.Z);
            //}
            //camera.Update(gameTime);

            float time = (float)gameTime.TotalGameTime.TotalSeconds * 0.333f;

            float cameraX = (float)Math.Cos(time);
            float cameraY = (float)Math.Sin(time);

            Vector3 cameraPosition = new Vector3(cameraX, 0, cameraY) * 64;
            Vector3 cameraFront = new Vector3(-cameraY, 0, cameraX);

            Matrix view = Matrix.CreateLookAt(cameraPosition,
                                              cameraPosition + cameraFront,
                                              Vector3.Up);
            camera.view = view;
            // Update models
            UpdateModels();
            
        }

        protected void UpdateModels()
        {
            // Loop through all models and call Update
            for (int i = 0; i < models.Count; ++i)
            {
                // Update each model
                models[i].Update();

                // Remove models that are out of bounds
                if (models[i].GetWorld().Translation.Z >
                    camera.cameraPosition.Z + 100)
                {
                    // If player has missed more than allowed, game over
                    ++missedThisLevel;
                    // Reset the kill count
                    consecutiveKills = 0;

                    models.RemoveAt(i);
                    --i;
                }
            }
        }

        public void Draw(GameTime gameTime)
        {
            //Game1.sb.Begin();
            //Game1.sb.Draw(Game1.txtSky, new Vector2(0,-300), Color.White);
            
            //Game1.sb.End();
            clsSky.Draw(camera.view, camera.projection);
            clsTerrain.Draw(camera.view, camera.projection);
            // Loop through and draw each model
            foreach (clsBasicModel bm in models)
            {
                bm.Draw(camera);
            }

            // Loop through and draw each shot
            foreach (clsBasicModel bm in shots)
            {
                bm.Draw(camera);
            }

            // Loop through and draw each particle explosion
            foreach (ParticleExplosion pe in explosions)
            {
                pe.Draw(camera);
            }

            // Draw the star sheet
            stars.Draw(camera);
        }

        private void SetNextSpawnTime()
        {
            // Reset the variables to indicate the next enemy spawn time
            nextSpawnTime = rand.Next(
                levelInfoList[currentLevel].minSpawnTime,
                levelInfoList[currentLevel].maxSpawnTime);
            timeSinceLastSpawn = 0;
        }

        protected void CheckToSpawnEnemy(GameTime gameTime)
        {
            // Time to spawn a new enemy?
            if (enemiesThisLevel <
                levelInfoList[currentLevel].numberEnemies)
            {
                timeSinceLastSpawn += gameTime.ElapsedGameTime.Milliseconds;
                if (timeSinceLastSpawn > nextSpawnTime)
                {
                    SpawnEnemy();
                }
            }
            else
            {
                if (explosions.Count == 0 && models.Count == 0)
                {
                    // ALL EXPLOSIONS AND SHIPS ARE REMOVED AND THE LEVEL IS OVER
                    ++currentLevel;
                    enemiesThisLevel = 0;
                    missedThisLevel = 0;
                    
                }
            }
        }

        private void SpawnEnemy()
        {
            // Generate random position with random X and random Y
            // between -maxX and maxX and -maxY and maxY. Z is always
            // the same for all ships.
            Vector3 position = new Vector3(rand.Next(
                -(int)maxSpawnLocation.X, (int)maxSpawnLocation.X),
                rand.Next(
                -(int)maxSpawnLocation.Y, (int)maxSpawnLocation.Y),
                maxSpawnLocation.Z);

            // Direction will always be (0, 0, Z), where
            // Z is a random value between minSpeed and maxSpeed
            Vector3 direction = new Vector3(0, 0,
                rand.Next(
                levelInfoList[currentLevel].minSpeed,
                levelInfoList[currentLevel].maxSpeed));

            // Get a random roll rotation between -maxRollAngle and maxRollAngle
            float rollRotation = (float)rand.NextDouble() *
                    maxRollAngle - (maxRollAngle / 2);

            // Add model to the list
            models.Add(new clsSpinningEnemy(
                game1.Content.Load<Model>(@"models\spaceship"),
                position, direction, 0, 0, rollRotation));

            // Increment # of enemies this level and set next spawn time
            ++enemiesThisLevel;
            SetNextSpawnTime();
        }
        

        protected void UpdateExplosions(GameTime gameTime)
        {
            // Loop through and update explosions
            for (int i = 0; i < explosions.Count; ++i)
            {
                explosions[i].Update(gameTime);
                // If explosion is finished, remove it
                if (explosions[i].IsDead)
                {
                    explosions.RemoveAt(i);
                    --i;
                }
            }
        }
    }
}
